package util;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.ResourceBundle;


import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;

import entities.CandidatoEntity;

import business.dto.CandidatoDTO;

/**
 *
 * @author salvatore.pannozzo
 */
public class DynamicObjectHandler {

    private static Logger logger = LogManager.getLogger(DynamicObjectHandler.class);
    private ResourceBundle rb = null;
    private String resourcePrefix = "";
    private ArrayList<String> headerParameters = new ArrayList<String>();

    private HashMap<String, Object> objectMap = new HashMap<String, Object>(); //Associa ad ogni nome di un metodo il suo valore

    public void setRb(ResourceBundle rb) {
        this.rb = rb;
    }

    public void setHeaderParameters(ArrayList<String> headerParameters) {
        this.headerParameters = headerParameters;
    }

    public String getResourcePrefix() {
        return resourcePrefix;
    }

    public void setResourcePrefix(String resourcePrefix) {
        this.resourcePrefix = resourcePrefix;
    }

    public DynamicObjectHandler() {
    }

    public DynamicObjectHandler(String resourcePrefix, ResourceBundle rb, ArrayList<String> hp) {
        this.resourcePrefix = resourcePrefix;
        this.rb = rb;
        this.headerParameters = hp;
    }

    public boolean fill(Object objectToFill, Map parametersList) throws Exception {
//         logger.info("filling :" + objectToFill.getClass().getSimpleName());
//         logger.info("with :" + parametersList);

        Class c = objectToFill.getClass();
        ArrayList<Method> methods = new ArrayList<Method>(Arrays.asList(c.getDeclaredMethods()));
//        String parameterName = "";

        ArrayList<Method> setterMethods = new ArrayList<Method>();// array di metodi set
        ArrayList<String> attributes = new ArrayList<String>(); // array di attributi
//        ArrayList values = new ArrayList(); // array di valore

        for (int i = 0; i < methods.size(); i++) {
            Method method = methods.get(i);
            String methodName = method.getName().substring(method.getName().lastIndexOf(".") + 1, method.getName().length());
            if (methodName.startsWith("set") && !methodName.endsWith("DTO")) {
                setterMethods.add(method);
                StringBuilder attribute = new StringBuilder(methodName.substring(3, 4).toLowerCase()).append(methodName.substring(4));
                attributes.add(attribute.toString());
            }
        }

        for (int i = 0; i < attributes.size(); i++) {
            String attribute = attributes.get(i);
            Method method = setterMethods.get(i);
            String methodName = method.getName().substring(method.getName().lastIndexOf(".") + 1, method.getName().length());
//          Method m = objectToFill.getClass().getMethod(methodName, method.getParameterTypes());

            Object parameter = parametersList.get(attribute);
//                logger.info("metodo :"+methodName+" with param :"+parameter);
            if (parameter != null) {
                try {
                	
                	Method m = objectToFill.getClass().getMethod(methodName, method.getParameterTypes()[0]);
                	logger.debug(objectToFill.getClass().getSimpleName()+"."+m.getName()+"("+method.getParameterTypes()[0]+")");
                	
                	Class parameterType=m.getParameterTypes()[0];
                	
                	
                	if(new String("").getClass().getSimpleName().equalsIgnoreCase(parameterType.getSimpleName())){
                		m.invoke(objectToFill, parameter.toString());
                	}else{ // BEGIN CONTROLLO SUI TIPI 
                		if(new Boolean(true).getClass().getSimpleName().equalsIgnoreCase(parameterType.getSimpleName())){
                    		Boolean booleanParameter=new Boolean(parameter.toString());
                    		m.invoke(objectToFill, booleanParameter);
                    		
                    	}
                    	if(new Integer(0).getClass().getSimpleName().equalsIgnoreCase(parameterType.getSimpleName())){
                    		Integer intParameter=new Integer(parameter.toString());
                    		m.invoke(objectToFill, intParameter);
                    		
                    	}
                    	if(new Date().getClass().getSimpleName().equalsIgnoreCase(parameterType.getSimpleName())){
                    		Date d=new Date();
                    		if(rb==null){
                    			rb= ResourceBundle.getBundle("bundle");
                    		}
                    		
                			String dateFormat=rb.getString("DATE_FORMAT");
                			
                    		SimpleDateFormat sdf=new SimpleDateFormat(dateFormat);
                    		
                    		d=sdf.parse(parameter.toString());
                    		m.invoke(objectToFill, d);
                    		
                    	}
                    	// END CONTROLLO SUI TIPI TODO TROVARE UN MODO PER FARE IL CASTING AUTOMATICO IN BASE AL TIPO DEL PARAMETRO

                	}
                	
                	
                	
                	

                	
                   
                } catch (Exception e) {
                    logger.error("errore nel lanciare il metodo "+methodName+", con il parametro "+parameter,e);
//                    logger.error(e.getCause());
                }

            }

        }

        return true;
    }

    public boolean fill(Object objectToFill, Object valueObject) { //throws Exception {
        Class c = valueObject.getClass();
        ArrayList<Method> methods = new ArrayList<Method>(Arrays.asList(c.getDeclaredMethods()));

        ArrayList<Method> getterMethods = new ArrayList<Method>();// array di metodi get
        ArrayList<String> attributes = new ArrayList<String>(); // array di attributi
        ArrayList values = new ArrayList(); // array di valore

        HashMap parameterList = new HashMap();
        
        for (int i = 0; i < methods.size(); i++) {
            Method method = methods.get(i);
            String methodName = method.getName().substring(method.getName().lastIndexOf(".") + 1, method.getName().length());
            if ((methodName.startsWith("get") || methodName.startsWith("is")) && !methodName.endsWith("DTO")) {
                getterMethods.add(method);
                StringBuilder attribute=null;
                if(methodName.startsWith("get")){
                	attribute = new StringBuilder(methodName.substring(3, 4).toLowerCase()).append(methodName.substring(4));
                }else{
                	attribute = new StringBuilder(methodName.substring(2, 3).toLowerCase()).append(methodName.substring(3));
                }
                
//                attributes.add(attribute.toString());
                Object value = new Object();
                try {
                	Method m = valueObject.getClass().getMethod(methodName, new Class[]{});
                    value = m.invoke(valueObject);
                    if(value!=null){
                    	values.add(value);
                    	if	(value instanceof Date ){
                    		
                    		if(rb==null){
                    			rb= ResourceBundle.getBundle("bundle");
                    		}
                    		
                			String dateFormat=rb.getString("DATE_FORMAT");
                			
                    		SimpleDateFormat sdf=new SimpleDateFormat(dateFormat);
                    		
                    		parameterList.put(attribute.toString(), sdf.format(value));
                    	}else{
                    		parameterList.put(attribute.toString(), value);
                    	}
                    	
                    }
                    
				} catch (Exception e) {
					logger.error("",e);
				}
                
                
            }
        }
        
//        for (int i = 0; i < values.size(); i++) {
//            Object value = values.get(i);
//            if (value != null) {
//                parameterList.put(attributes.get(i), value);
//            }
//        }
        try {
        	return fill(objectToFill, parameterList);
		} catch (Exception e) {
			// TODO: handle exception
			return false;
		}
        

    }

    public boolean update(Object objectToUpdate, Object valueObject) throws Exception {
        Class c = valueObject.getClass();
        ArrayList<Method> methods = new ArrayList<Method>(Arrays.asList(c.getDeclaredMethods()));

        ArrayList<Method> getterMethods = new ArrayList<Method>();// array di metodi get
        ArrayList<String> attributes = new ArrayList<String>(); // array di attributi
        ArrayList values = new ArrayList(); // array di valore

        HashMap parameterList = new HashMap();
        
        for (int i = 0; i < methods.size(); i++) {
            Method method = methods.get(i);
            String methodName = method.getName().substring(method.getName().lastIndexOf(".") + 1, method.getName().length());
            if ((methodName.startsWith("get")|| methodName.startsWith("is")) && !methodName.endsWith("DTO")) {
                getterMethods.add(method);
                StringBuilder attribute=new StringBuilder();
                if(methodName.startsWith("get")){
                	attribute = new StringBuilder(methodName.substring(3, 4).toLowerCase()).append(methodName.substring(4));
                }else{
                	attribute = new StringBuilder(methodName.substring(2, 3).toLowerCase()).append(methodName.substring(3));
                }
                
                Object value = new Object();
                Method m = valueObject.getClass().getMethod(methodName, new Class[]{});
                try {
                    value = m.invoke(valueObject);
                    
                    if(value!=null){
                    	values.add(value);
                    	if	(value instanceof Date ){
                    			SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
                    			parameterList.put(attribute.toString(), sdf.format(value));
                    	}else{
                    		parameterList.put(attribute.toString(), value);
                    	}
                    	
                    }
                    
                } catch (Exception e) {
                    logger.error("errore nel lanciare il metodo "+methodName+", con il parametro "+attribute.toString(),e);
                }
            }
        }
        
          
        
        return fill(objectToUpdate, parameterList);
    }

    public String toString(Object objectToPrint) throws Exception {
        // logger.info("printing :" + objectToPrint.getClass().getSimpleName());


        StringBuilder sb = new StringBuilder();
//        logger.info("YYYYYYYYYYYYYY: "+headerParameters.get(0));

        String className = objectToPrint.getClass().getSimpleName();
        //logger.info("printing :" + className);

        Class c = objectToPrint.getClass();
        ArrayList<Method> methods = new ArrayList<Method>(Arrays.asList(c.getDeclaredMethods()));

        ArrayList<Method> getterMethods = new ArrayList<Method>();// array di metodi get
        ArrayList<String> attributes = new ArrayList<String>(); // array di attributi
        ArrayList values = new ArrayList(); // array di valore

        for (int i = 0; i < methods.size(); i++) {
            Method method = methods.get(i);
            String methodName = method.getName().substring(method.getName().lastIndexOf(".") + 1, method.getName().length());
            if (methodName.startsWith("get") && !methodName.endsWith("DTO")) {
                //getterMethods.add(method);

                StringBuilder attribute = new StringBuilder(methodName.substring(3, 4).toLowerCase()).append(methodName.substring(4));
                attributes.add(attribute.toString());

                Object value = new Object();
                Method m = objectToPrint.getClass().getMethod(methodName, new Class[]{});
                value = m.invoke(objectToPrint);
                if (headerParameters.contains(attribute.toString()) && value == null) {
                    value = "";
                }
                values.add(value);
            }
        }


        sb.append("<").append(className);
        for (int i = 0; i < values.size(); i++) {
            Object value = values.get(i);
            //logger.info(object.getClass());
            if (value != null) {
                //if (!value.equals("")) {
                //logger.info("CCCCCCCCCCC "+value);
                //                 logger.info("attributo: "+attributes.get(i)+" "+headerParameters.contains(attributes.get(i).trim())+" "+headerParameters.get(0));
                if (headerParameters.contains(attributes.get(i))) {

                    //logger.info("DDDDDDDDDD "+attributes.get(i)+"="+value);
                    sb.append(" ").append(attributes.get(i)).append("='").append(value).append("'");
                }
                //}
            }
        }
        sb.append(">\n");
        String label = "";
        for (int i = 0; i < values.size(); i++) {
            Object value = values.get(i);
            if (value != null) {
                if (!value.equals("") && !attributes.get(i).equals("id") && !attributes.get(i).equals("pims")
                        && !attributes.get(i).equals("customFields") && !attributes.get(i).equals("bid")) {

                    try {
                        label = rb.getString(resourcePrefix + "_" + attributes.get(i).toString().toUpperCase());
                    } catch (Exception e) {
                               logger.error("Impossibile trovare la risorsa: " + resourcePrefix + "_" + attributes.get(i).toString().toUpperCase(), e);
                        //       logger.error(e.getCause());
                    }
                    sb.append("\t<parametro ").append("name='").append(attributes.get(i)).append("' ").
                            append("label='").append(label).append("' value='").
                            append(value.toString()).append("' />\n");
                }
                if (!value.equals("") && attributes.get(i).equals("customFields")) {
                    sb.append(value);
                }
                if (!value.equals("") && attributes.get(i).equals("pims")) {
                    try {
                        label = rb.getString(resourcePrefix + "_" + attributes.get(i).toString().toUpperCase());
                    } catch (Exception e) {
                        logger.error("Impossibile trovare la risorsa: " + resourcePrefix + "_" + attributes.get(i).toString().toUpperCase(), e);
                        //logger.error(e.getCause());
                    }
                    ArrayList pims = new ArrayList((Collection) value);
                    sb.append("\t<parametro ").append("name='").append(attributes.get(i)).append("' ").
                            append("label='").append(label).append("' value=' ").append(pims.size()).append("' />");
                }
            }
        }
        sb.append("</").append(className).append(">");
        return sb.toString();
    }

    public HashMap<String, Object> getObjectMap(Object objectToAnalyze) throws Exception {// ritorna associazione tra nomecampo e valore
        objectMap=new HashMap<String, Object>();
        Class c = objectToAnalyze.getClass();
        ArrayList<Method> methods = new ArrayList<Method>(Arrays.asList(c.getDeclaredMethods()));
        
        for (int i = 0; i < methods.size(); i++) {
            Method method = methods.get(i);
            String methodName = method.getName().substring(method.getName().lastIndexOf(".") + 1, method.getName().length());
            if (methodName.startsWith("get") && !methodName.endsWith("DTO") &&
                    !methodName.equals("getPimType") && !methodName.equals("getId") && !methodName.equals("getBid")
                    && !methodName.equals("getDataAcq")  && !methodName.equals("getCustomFields") && !methodName.equals("getFileInput")) {


                StringBuilder attribute = new StringBuilder(methodName.substring(3, 4).toLowerCase()).append(methodName.substring(4));
                Object value = new Object();
                try {
                    Method m = objectToAnalyze.getClass().getMethod(methodName, new Class[]{});
                    value = m.invoke(objectToAnalyze);
                    if (value!=null && !value.equals("")) {
                        objectMap.put(attribute.toString(), value);
                    }
                    
                } catch (Exception e) {
                }
            }
        }


        return objectMap;
    }
    public HashMap<String, Object> getObjectMap(Object objectToAnalyze,ArrayList<String> fieldsToAvoid) throws Exception {// ritorna associazione tra nomecampo e valore
        objectMap=new HashMap<String, Object>();
        Class c = objectToAnalyze.getClass();
        ArrayList<Method> methods = new ArrayList<Method>(Arrays.asList(c.getDeclaredMethods()));
        


        for (int i = 0; i < methods.size(); i++) {
            Method method = methods.get(i);
            String methodName = method.getName().substring(method.getName().lastIndexOf(".") + 1, method.getName().length());
            if (methodName.startsWith("get") && !methodName.endsWith("DTO") &&
                    !methodName.equals("getPimType") && !methodName.equals("getId") && !methodName.equals("getBid")
                    && !methodName.equals("getDataAcq")  && !methodName.equals("getCustomFields") && !methodName.equals("getFileInput")) {


                StringBuilder attribute = new StringBuilder(methodName.substring(3, 4).toLowerCase()).append(methodName.substring(4));


                if (!fieldsToAvoid.contains(attribute.toString())) {
                    Object value = new Object();
                    try {
                        Method m = objectToAnalyze.getClass().getMethod(methodName, new Class[]{});
                        value = m.invoke(objectToAnalyze);
                        if (value!=null && !value.equals("")) {
                            objectMap.put(attribute.toString(), value);
                        }

                    } catch (Exception e) {
                    }
                }
            }
        }
        return objectMap;
    }
    public static void main(String[] args){
    	CandidatoDTO cDTO=new CandidatoDTO();
    	
    	DTOClass dtoClass=new DTOClass();
    	BOClass boClass=new BOClass();
    	
    	HashMap parametersMap=new HashMap();
    	
    	parametersMap.put("name","salvatore");
    	parametersMap.put("cognome","pannozzo");
    	parametersMap.put("incoming",true);
    	
    	parametersMap.put("contratto","progetto");
    	parametersMap.put("dateStart","2013-06-01");
    	
    	DynamicObjectHandler doh=new DynamicObjectHandler();
    	CandidatoEntity ce=new CandidatoEntity();
    	try {
//			doh.fill(cDTO, parametersMap);
//			doh.fill(ce,cDTO);
			
    		doh.fill(dtoClass, parametersMap);
    		doh.fill(boClass,dtoClass);
    		
    		dtoClass.setDateStart("2013-05-31");
    		
    		System.out.println(boClass.toString());
    		
    		doh.update(boClass,dtoClass);
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    	
//    	System.out.println(ce.getDTO().toString());
    	
    	
    	System.out.println(boClass.toString());
    	
    	
    }
}

class DTOClass{
	private String name;
	private String cognome;
	private String incoming;
	private String age;
	private String dateStart;
	
	public String getDateStart() {
		return dateStart;
	}
	public void setDateStart(String dateStart) {
		this.dateStart = dateStart;
	}
	public String getAge() {
		return age;
	}
	public void setAge(String age) {
		this.age = age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getCognome() {
		return cognome;
	}
	public void setCognome(String cognome) {
		this.cognome = cognome;
	}
	public String getIncoming() {
		return incoming;
	}
	public void setIncoming(String incoming) {
		this.incoming = incoming;
	}
}
class BOClass{
	private String name;
	private String cognome;
	private boolean incoming;
	private int age;
	private Date dateStart;
	
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getCognome() {
		return cognome;
	}
	public void setCognome(String cognome) {
		this.cognome = cognome;
	}
	public boolean isIncoming() {
		return incoming;
	}
	public void setIncoming(boolean incoming) {
		this.incoming = incoming;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public Date getDateStart() {
		return dateStart;
	}
	public void setDateStart(Date dateStart) {
		this.dateStart = dateStart;
	}
	
	@Override
	public String toString() {
		return "BOClass [name=" + name + ", cognome=" + cognome + ", incoming="
				+ incoming + ", age=" + age + ", dateStart=" + dateStart + "]";
	}
		
}